(function($) {

	KOR.carousel = function(selector, mixin) {
		return KOR.Carousel.getInstanceOf(selector, mixin);
	};

	KOR.Carousel = KOR.createClass(KOR.UI, {

		NAME: 'carousel',

		/**
		 * Tells whether this slider is horizontal or verticals
		 */
		horizontal: true,
		animationDuration: 500,
		paginationSelector: '',
		previousSelector: '.kor-control-previous',
		nextSelector: '.kor-control-next',
		maskSelector: '.kor-mask',
		disabledClass: 'kor-control-disabled',
		previousDisabledClass: 'kor-control-previous-disabled',
		nextDisabledClass: 'kor-control-next-disabled',
		paginationDisabledClass: 'kor-control-pagination-disabled',
		paginationSelectedClass: 'kor-control-pagination-selected',
		autoAdvanceTime: false,

		/**
		 * Number of items to increment by
		 */
		increment: 1,

		/**
		 * Registers events for the Component Wrapper
		 *
		 * @returns
		 */
		registerEvents: function() {
			this.onDomLoad = this.autoAdvanceTime;
			this._super();

			var obj = this;
			$(document).delegate(obj.selector, 'mouseenter', $.proxy(obj, "mouseoverHandler"));
			$(document).delegate(obj.selector, 'mouseleave', $.proxy(obj, 'mouseLeaveHandler'));
			$(document).delegate(obj.selector + " " + obj.nextSelector, "click", $.proxy(obj, "nextHandler"));
			$(document).delegate(obj.selector + " " + obj.previousSelector, "click", $.proxy(obj, "previousHandler"));

			$(document).delegate(obj.selector + " " + obj.nextSelector, obj.NAME+':next', $.proxy(obj, "nextAction"));
			$(document).delegate(obj.selector + " " + obj.previousSelector, obj.NAME+':previous', $.proxy(obj, "previousAction"));

			if(obj.paginationSelector){
				$(document).delegate(obj.selector + " " + obj.paginationSelector, 'click', $.proxy(obj, "paginationAction"));
			}
			if(obj.autoAdvanceTime){
				$(document).delegate(obj.selector, obj.NAME+':autoAdvance', $.proxy(obj, "autoAdvanceHandler"));
			}
		},

		domLoadedHandler: function(ev){
			this.setTimers($(ev.currentTarget));
		},

		setTimers: function(node){
			if(this.autoAdvanceTime){
				var obj = this;
				this.autoAdvanceTimer = setInterval(function(){
					node.data('autoAdvanceFlag','true');
					node.trigger(obj.NAME+':autoAdvance', [node]);
				}, this.autoAdvanceTime)
			}
		},

		clearTimers: function(node){
			node.removeData('autoAdvanceFlag');
			clearInterval(this.autoAdvanceTimer);
		},

		autoAdvanceHandler: function(ev, node){
			node = node || $(ev.currentTarget);
			if(node.data('autoAdvanceFlag') && !node.data('mouseover')){
				node.removeData('autoAdvanceFlag');
				this.nextAction(ev);
			}
		},

		getIndex: function(holder, items, dir, node){
			var index = holder.data(this.NAME+'Index');
			if (!index || isNaN(index)) {
				index = 0;
			}

			if (dir == "next") {
				index = index + this.increment;
			} else {
				index = index - this.increment;
			}

			if(this.autoAdvanceTime && index > items.length -1){
				index = 0;
			}

			index = Math.max(0, Math.min(items.length - 1, index));

			if (dir == 'page'){
				index = parseInt(node.prevAll().length);
			}
			return index;
		},

		/**
		 * index is 0 based
		 */
		moveTo: function(node, dir) {
			if (node.hasClass(this.disabledClass)) {
				return;
			}
			var holder = node.closest(this.selector);
				next = holder.children(this.nextSelector);
				previous = holder.children(this.previousSelector);
				pagination = holder.find(this.paginationSelector);
				container = holder.children(this.maskSelector).children().first();
				items = container.children("li");
				index = this.getIndex(holder, items, dir, node);

			this.clearTimers(holder);
			this.disableControls(holder, next, previous, pagination, container, items, index, dir);
			this.move(holder, next, previous, pagination, container, items, index, dir);

			this.setTimers(holder);
			holder.data(this.NAME+'Index', index);
		},

		move: function(holder, next, previous, pagination, container, items, index, dir){
			if (this.horizontal) {
				this.moveHorizontal.apply(this,arguments);
			} else {
				this.moveVertical.apply(this,arguments);
			}
		},

		moveHorizontal: function(holder, next, previous, pagination, container, items, index, dir){
			var obj = this;
			var args = arguments;
			var containerWidth = 0;
			var allowance = 0;
			$.each(items, function(i,v){
				var curWidth = $(v).width();
				if(i==0){
					allowance = curWidth;
				}else if(curWidth < allowance){
					allowance=curWidth;
				}
				containerWidth+=$(v).outerWidth(true);
			})
			// set the width of the UL in case it is not set in the CSS
			container.width(containerWidth);

			var left = 0;
			for (var i = 0; i<index; i++){
				left += $(items[i]).outerWidth(true);
			}
			left = left * -1;

			container.animate( {
				left: left + "px"
			}, {
				duration: obj.animationDuration,
				complete: function() {
					obj.setControls.apply(obj,args);
				}
			});
		},

		moveVertical: function(holder, next, previous, pagination, container, items, index, dir){
			var obj = this;
			var args = arguments;
			var containerHeight = 0;
			var allowance = 0;
			$.each(items, function(i,v){
				var curHeight = $(v).height();
				if(i==0){
					allowance = curHeight;
				}else if(curHeight < allowance){
					allowance=curHeight;
				}
				containerHeight+=$(v).outerHeight(true);
			})

			// set the width of the UL in case it is not set in the CSS
			container.height(containerHeight);

			var top = 0;
			for (var i = 0; i<index; i++){
				top += $(items[i]).outerHeight(true);
			}
			top = top * -1;

			container.animate( {
				top: top + "px"
			}, {
				duration: obj.animationDuration,
				complete: function(){
					obj.setControls.apply(obj,args);
				}

			});
		},

		disableControls: function(holder, next, previous, pagination, container, items, index, dir){
			pagination.removeClass(this.paginationSelectedClass)
				.addClass(this.disabledClass +' '+ this.paginationDisabledClass)
				.eq(index).addClass(this.paginationSelectedClass);
			previous.addClass(this.disabledClass +' '+ this.previousDisabledClass);
			next.addClass(this.disabledClass +' '+ this.nextDisabledClass);
		},

		setControls: function(holder, next, previous, pagination, container, items, index){
			next.removeClass(this.disabledClass +' '+ this.nextDisabledClass);
			previous.removeClass(this.disabledClass +' '+ this.previousDisabledClass);

			if (index == 0) {
				previous.addClass(this.disabledClass +' '+ this.previousDisabledClass);
			}
			if (index == items.length - 1) {
				next.addClass(this.disabledClass +' '+ this.nextDisabledClass);
			}

			this.setPagination.apply(this,arguments);

		},

		setPagination: function(holder, next, previous, pagination, container, items, index, dir){
			if (this.paginationSelector) {
				pagination.removeClass(this.disabledClass +' '+ this.paginationDisabledClass);
				pagination.eq(index).addClass(this.paginationSelectedClass)
			}
		},

		next: function(node, event) {
			this.moveTo(node, "next");
		},

		previous: function(node, event) {
			this.moveTo(node, "previous");
		},

		paginate: function(node, event) {
			this.moveTo(node, "page")
		},

		/**
		 * Delegates the event to the :click event type.
		 *
		 * @param event
		 * @returns
		 */
		nextHandler: function(event) {
			var node = $(event.currentTarget);
			node.trigger(this.NAME+':next', this);
            return false
		},

		/**
		 * Delegates the event to the :click event type.
		 *
		 * @param event
		 * @returns
		 */
		previousHandler: function(event) {
			var node = $(event.currentTarget);
			node.trigger(this.NAME+':previous', this);
            return false
		},

		mouseoverHandler: function(event) {
			var node = $(event.currentTarget);
			node.data('mouseover', 'true')
			return false;
		},

		mouseLeaveHandler: function(event){
			var node = $(event.currentTarget);
			node.removeData('mouseover');
			if(this.autoAdvanceTime){
				node.trigger(this.NAME+':autoAdvance');
			}
			return false;
		},

		mouseoverAction: function(event, data) {
			var node = $(event.currentTarget).closest(this.selector);
			var container = node.children(this.maskSelector).children().first();
			var items = container.children("li");
			items.css({
				'width': items.css('width'),
				'margin-left': items.css('margin-left'),
				'margin-right': items.css('margin-right')
			})
		},

		/**
		 * Handles a Next event.
		 *
		 * @param event
		 * @param data
		 * @returns
		 */
		nextAction: function(event, data) {
			var node = $(event.currentTarget);
			this.next(node, event);
		},

		/**
		 * Handles a Previous event.
		 *
		 * @param event
		 * @param data
		 * @returns
		 */
		previousAction: function(event, data) {
			var node = $(event.currentTarget);
			this.previous(node, event);
		},

		paginationAction: function(event, data) {
			var node = $(event.currentTarget);
			this.paginate(node, event);
		}
	});

})(jQuery);
